package com.kyin.satoken.filter;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kyin.satoken.utils.CryptoUtil;
import org.apache.http.protocol.HTTP;
import org.springframework.http.HttpMethod;
import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/*
 这个是一个自定义的HttpServletRequestWrapper，用于封装原始请求参数
 1. 封装原始请求参数
 2. 不会把原来请求的参数一起传给后面的controller
 */
public class RequestParamsRequestWrapper extends HttpServletRequestWrapper {

    private String magicPrefix;
    private static final String MAGIC_URL_KEY = "reqUrl";
    // 存储解密后的新参数
    private Map<String, String[]> extraParams;
    private byte[] body;
    private boolean isJsonRequest;
    private ObjectMapper objectMapper = new ObjectMapper();
    private JSONObject jsonBody;

    public RequestParamsRequestWrapper(HttpServletRequest request,String magicPrefix) throws IOException {
        super(request);
        this.extraParams = new HashMap<>();
        this.magicPrefix = magicPrefix;

        // 1. 处理GET请求原始参数
        if (HttpMethod.GET.name().equalsIgnoreCase(request.getMethod())) {
            handleGetOriginalParams(request);
        }
        // 2. 处理POST请求原始参数
        else if (HttpMethod.POST.name().equalsIgnoreCase(request.getMethod())) {
            cacheRequestBody(request);
            if (isFormContentType(request)) {
                handleFormOriginalParams();
            } else if (isJsonContentType(request)) {
                handleJsonOriginalParams();
            }
        }
    }

    /**
     * 处理GET请求的原始参数
     */
    private void handleGetOriginalParams(HttpServletRequest request) {
        // 获取所有原始GET参数
        Map<String, String[]> originalParams = super.getParameterMap();

        Map<String, Object> originalParamMaps = new HashMap<>();

        originalParams.forEach((key, value) -> {
            originalParamMaps.put(key, value[0]);
        });

        Map<String, Object> reqDecryptParams = getReqDecryptParams(originalParamMaps);
        // 遍历原始参数进行解密并添加
        reqDecryptParams.forEach((key, value) ->  addParamValue(key, value.toString()));

    }

    /**
     * 处理表单类型POST请求的原始参数
     */
    private void handleFormOriginalParams() throws UnsupportedEncodingException {
        if (body == null || body.length == 0) return;

        // 解析原始表单参数
        String bodyStr = new String(body, getCharacterEncoding());
        String[] pairs = bodyStr.split("&");
        // 原始加密参数
        Map<String, Object> originalParamMaps = new HashMap<>();
        for (String pair : pairs) {
            String[] kv = pair.split("=", 2);
            if (kv.length == 2) {
                originalParamMaps.put(kv[0], kv[1]);
            }
        }

        Map<String, Object> reqDecryptParams = getReqDecryptParams(originalParamMaps);
        // 遍历原始参数进行解密并添加
        reqDecryptParams.forEach((key, value) ->  addParamValue(key, value.toString()));

        // 重置请求体为解密后的参数（表单类型）
        resetFormBody();
    }

    /**
     * 处理JSON类型POST请求的原始参数
     */
    private void handleJsonOriginalParams() throws UnsupportedEncodingException {
        this.isJsonRequest = true;
        this.jsonBody = new JSONObject();

        if (body == null || body.length == 0) return;

        // 解析原始JSON参数
        String jsonBodyStr = new String(body, getCharacterEncoding());
        Map<String,Object> originalParamJson = JSONUtil.parseObj(jsonBodyStr);
        Map<String, Object> reqDecryptParams = getReqDecryptParams(originalParamJson);
        reqDecryptParams.forEach((key, value) ->  {
            // 添加解密后的参数到JSON对象
            jsonBody.put(key, value);
            addParamValue(key, value.toString());
        });
        // 重置请求体为解密后的JSON
        updateBodyFromJson();
    }

    /**
     * 重置表单类型的请求体为解密后的参数
     */
    private void resetFormBody() {
        try {
            StringBuilder formBody = new StringBuilder();
            extraParams.forEach((key, values) -> {
                for (String value : values) {
                    if (formBody.length() > 0) {
                        formBody.append("&");
                    }
                    formBody.append(key).append("=").append(value);
                }
            });
            this.body = formBody.toString().getBytes(StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("重置表单请求体失败", e);
        }
    }

    // 以下是解密相关方法，需要根据实际加密方式实现
    private String decrypt(String value) {
        // 示例：这里只是返回原值，实际应用中替换为真实解密逻辑
        // 如：AES解密、RSA解密等
        return value;
    }

    private Object decryptObject(Object value) {
        if (value instanceof String) {
            return decrypt((String) value);
        } else if (value instanceof List) {
            List<?> list = (List<?>) value;
            List<Object> decryptedList = new ArrayList<>();
            for (Object item : list) {
                decryptedList.add(decryptObject(item));
            }
            return decryptedList;
        } else if (value instanceof JSONObject) {
            JSONObject json = (JSONObject) value;
            JSONObject decryptedJson = new JSONObject();
            for (Map.Entry<String, Object> entry : json.entrySet()) {
                decryptedJson.put(decrypt(entry.getKey()), decryptObject(entry.getValue()));
            }
            return decryptedJson;
        }
        return value;
    }

    // 以下为原有核心方法，保持不变
    @Override
    public ServletInputStream getInputStream() {
        return new ByteArrayServletInputStream(this.body);
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(
                new ByteArrayInputStream(this.body),
                StandardCharsets.UTF_8));
    }

    static class ByteArrayServletInputStream extends ServletInputStream {
        private final ByteArrayInputStream buffer;

        public ByteArrayServletInputStream(byte[] contents) {
            this.buffer = new ByteArrayInputStream(contents);
        }

        @Override
        public int read() { return buffer.read(); }
        @Override
        public boolean isFinished() { return buffer.available() == 0; }
        @Override
        public boolean isReady() { return true; }
        @Override
        public void setReadListener(ReadListener listener) { }
    }

    @Override
    public String getParameter(String name) {
        String[] values = extraParams.get(name);
        return values != null && values.length > 0 ? values[0] : null;
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return new HashMap<>(extraParams);
    }

    @Override
    public Enumeration<String> getParameterNames() {
        return Collections.enumeration(extraParams.keySet());
    }

    @Override
    public String[] getParameterValues(String name) {
        return extraParams.get(name);
    }

    public void addParameter(String name, String... values) {
        extraParams.put(name, values);
        if (isJsonRequest && jsonBody != null) {
            if (values.length == 1) {
                jsonBody.put(name, values[0]);
            } else {
                jsonBody.put(name, values);
            }
            updateBodyFromJson();
        }
    }

    public String getJsonBody() {
        return jsonBody != null ? jsonBody.toString() : null;
    }

    private void updateBodyFromJson() {
        try {
            this.body = objectMapper.writeValueAsBytes(jsonBody);
        } catch (Exception e) {
            throw new RuntimeException("JSON序列化失败", e);
        }
    }

    private void cacheRequestBody(HttpServletRequest request) throws IOException {
        this.body = StreamUtils.copyToByteArray(request.getInputStream());
        if (isJsonContentType(request) && (this.body == null || this.body.length == 0)) {
            this.body = "{}".getBytes(StandardCharsets.UTF_8);
        }
    }

    private boolean isFormContentType(HttpServletRequest request) {
        String contentType = request.getContentType();
        return contentType != null && (
                contentType.contains("x-www-form-urlencoded") ||
                        contentType.contains("multipart/form-data")
        );
    }

    private boolean isJsonContentType(HttpServletRequest request) {
        String contentType = request.getContentType();
        return contentType != null && contentType.contains("application/json");
    }

    private void addParamValue(String name, String value) {
        String[] values = extraParams.get(name);
        if (values == null) {
            extraParams.put(name, new String[]{value});
        } else {
            String[] newValues = Arrays.copyOf(values, values.length + 1);
            newValues[values.length] = value;
            extraParams.put(name, newValues);
        }
    }

    // 解密参数
    private Map<String,Object> getReqDecryptParams(Map<String, Object> reqParams)  {
        String decryptedParam = CryptoUtil.decryptSecurePayload(reqParams);
        Map<String,Object> paramMap= JSONUtil.parseObj(decryptedParam);
        if (paramMap.containsKey(MAGIC_URL_KEY)) {
            String reqUrl = MapUtil.getStr(paramMap,MAGIC_URL_KEY,"");
            reqUrl = StrUtil.replace(reqUrl,magicPrefix,"");
            paramMap.put(MAGIC_URL_KEY, reqUrl);
        }
        return paramMap;
    }
}
